Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Node / ReticulumProjects / MeshChatX.git / files / tests / backend / test_integrity_extensive.py

Displaying Raw • Download


tests/backend/test_integrity_extensive.py 66412c74389b272bbd7ced2f5071396e5031a59f (66412c74) Text, 16.70 KB

Tff7b72import T7ee787json
Tff7b72import T7ee787os
Tff7b72import T7ee787shutil
Tff7b72import T7ee787sqlite3
Tff7b72import T7ee787tempfile
Tff7b72import T7ee787unittest
Tff7b72from T7ee787pathlib Tff7b72import Te6edf3Path

Tff7b72from T7ee787hypothesis Tff7b72import Te6edf3HealthCheckTb4b4b4, Te6edf3givenTb4b4b4, Te6edf3settings
Tff7b72from T7ee787hypothesis Tff7b72import Te6edf3strategies Tff7b72as Te6edf3st

Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787integrity_manager Tff7b72import Te6edf3IntegrityManager


Tff7b72class T56d364TestIntegrityManagerExtensiveTb4b4b4(Te6edf3unittestTff7b72.Td2a8ffTestCaseTb4b4b4)Tb4b4b4:
Tff7b72def Td2a8ffsetUpTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72= Te6edf3PathTb4b4b4(Te6edf3tempfileTff7b72.Td2a8ffmkdtempTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffdb_path Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffdatabase.dbTa5d6ff"
Tff7b72selfTff7b72.Td2a8ffstorage_dir Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffstorageTa5d6ff"
Tff7b72selfTff7b72.Td2a8ffstorage_dirTff7b72.Td2a8ffmkdirTb4b4b4(Tb4b4b4)

T8b949e# Create a valid SQLite database
Te6edf3conn Tff7b72= Te6edf3sqlite3Tff7b72.Td2a8ffconnectTb4b4b4(Tff7b72selfTff7b72.Td2a8ffdb_pathTb4b4b4)
Te6edf3connTff7b72.Td2a8ffexecuteTb4b4b4(Ta5d6ff"Ta5d6ffCREATE TABLE data (id INTEGER PRIMARY KEY, val TEXT)Ta5d6ff"Tb4b4b4)
Te6edf3connTff7b72.Td2a8ffexecuteTb4b4b4(Ta5d6ff"Ta5d6ffINSERT INTO data (val) VALUES (Ta5d6ff'Ta5d6ffinitialTa5d6ff'Ta5d6ff)Ta5d6ff"Tb4b4b4)
Te6edf3connTff7b72.Td2a8ffcommitTb4b4b4(Tb4b4b4)
Te6edf3connTff7b72.Td2a8ffcloseTb4b4b4(Tb4b4b4)

Tff7b72selfTff7b72.Td2a8ffmanager Tff7b72= Te6edf3IntegrityManagerTb4b4b4(Tff7b72selfTff7b72.Td2a8fftest_dirTb4b4b4, Tff7b72selfTff7b72.Td2a8ffdb_pathTb4b4b4)

Tff7b72def Td2a8fftearDownTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
Te6edf3shutilTff7b72.Td2a8ffrmtreeTb4b4b4(Tff7b72selfTff7b72.Td2a8fftest_dirTb4b4b4)

Tff7b72def Td2a8fftest_entropy_mathematical_boundsTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Verify entropy stays within [0, 8] for any byte sequence."""
T8b949e# Test empty
Te6edf3empty_file Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffemptyTa5d6ff"
Te6edf3empty_fileTff7b72.Td2a8fftouchTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertEqualTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_calculate_entropyTb4b4b4(Te6edf3empty_fileTb4b4b4)Tb4b4b4, T79c0ff0Tb4b4b4)

T8b949e# Test single byte repeated (minimum entropy)
Te6edf3zero_entropy_file Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffzero_entropyTa5d6ff"
Tff7b72with Tffa657openTb4b4b4(Te6edf3zero_entropy_fileTb4b4b4, Ta5d6ff"Ta5d6ffwbTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3fTff7b72.Td2a8ffwriteTb4b4b4(Ta5d6ffbTa5d6ff"Ta5d6ffAAAAAAAATa5d6ff" Tff7b72* T79c0ff100Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertEqualTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_calculate_entropyTb4b4b4(Te6edf3zero_entropy_fileTb4b4b4)Tb4b4b4, T79c0ff0Tb4b4b4)

T8b949e# Test all 256 bytes (maximum entropy)
Te6edf3max_entropy_file Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffmax_entropyTa5d6ff"
Tff7b72with Tffa657openTb4b4b4(Te6edf3max_entropy_fileTb4b4b4, Ta5d6ff"Ta5d6ffwbTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3fTff7b72.Td2a8ffwriteTb4b4b4(Tffa657bytesTb4b4b4(Tffa657rangeTb4b4b4(T79c0ff256Tb4b4b4)Tb4b4b4)Tb4b4b4)
T8b949e# log2(256) = 8
Tff7b72selfTff7b72.Td2a8ffassertAlmostEqualTb4b4b4(
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_calculate_entropyTb4b4b4(Te6edf3max_entropy_fileTb4b4b4)Tb4b4b4,
T79c0ff8.0Tb4b4b4,
Te6edf3placesTff7b72=T79c0ff5Tb4b4b4,
Tb4b4b4)

Tf0883e@settingsTb4b4b4(Te6edf3suppress_health_checkTff7b72=Tb4b4b4[Te6edf3HealthCheckTff7b72.Td2a8fftoo_slowTb4b4b4]Tb4b4b4, Te6edf3deadlineTff7b72=Tff7b72NoneTb4b4b4)
Tf0883e@givenTb4b4b4(Te6edf3stTff7b72.Td2a8ffbinaryTb4b4b4(Te6edf3min_sizeTff7b72=T79c0ff1Tb4b4b4, Te6edf3max_sizeTff7b72=T79c0ff1024Tb4b4b4)Tb4b4b4)
Tff7b72def Td2a8fftest_entropy_propertyTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3dataTb4b4b4)Tb4b4b4:
T8b949e"""Property: Entropy is always between 0 and 8 for non-empty data."""
Te6edf3temp_file Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffprop_testTa5d6ff"
Tff7b72with Tffa657openTb4b4b4(Te6edf3temp_fileTb4b4b4, Ta5d6ff"Ta5d6ffwbTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3fTff7b72.Td2a8ffwriteTb4b4b4(Te6edf3dataTb4b4b4)

Te6edf3entropy Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_calculate_entropyTb4b4b4(Te6edf3temp_fileTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertGreaterEqualTb4b4b4(Te6edf3entropyTb4b4b4, T79c0ff0Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertLessEqualTb4b4b4(Te6edf3entropyTb4b4b4, T79c0ff8.000000000000002Tb4b4b4) T8b949e# Float precision

Tff7b72def Td2a8fftest_db_structural_tamper_detectionTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Simulate actual SQLite corruption that bypasses hash-only checks."""
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)

T8b949e# Corrupt the database file header or internal structure
T8b949e# Overwriting the first few bytes (SQLite header) is a guaranteed fail
Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffdb_pathTb4b4b4, Ta5d6ff"Ta5d6ffr+bTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3fTff7b72.Td2a8ffseekTb4b4b4(T79c0ff0Tb4b4b4)
Te6edf3fTff7b72.Td2a8ffwriteTb4b4b4(Ta5d6ffbTa5d6ff"Ta5d6ffNOTASQLITEFILETa5d6ff"Tb4b4b4)

Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(Te6edf3is_okTb4b4b4, Ta5d6fffTa5d6ff"Ta5d6ffIntegrity should fail. Issues: Tffd700{Te6edf3issuesTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(
Tffa657anyTb4b4b4(
Ta5d6ff"Ta5d6ffDatabase structural issueTa5d6ff" Tff7b72in Te6edf3i Tff7b72or Ta5d6ff"Ta5d6ffDatabase structural anomalyTa5d6ff" Tff7b72in Te6edf3i
Tff7b72for Te6edf3i Tff7b72in Te6edf3issues
Tb4b4b4)Tb4b4b4,
Ta5d6fffTa5d6ff"Ta5d6ffExpected structural issue in: Tffd700{Te6edf3issuesTffd700}Ta5d6ff"Tb4b4b4,
Tb4b4b4)

Tff7b72def Td2a8fftest_entropy_shift_detectionTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Test detection of content type change (e.g. replacing text with random bytes)."""
T8b949e# 1. Start with a highly structured file (low entropy)
T8b949e# Use a non-critical filename to trigger the entropy check branch
Te6edf3data_file Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffuser_data.binTa5d6ff"
Tff7b72with Tffa657openTb4b4b4(Te6edf3data_fileTb4b4b4, Ta5d6ff"Ta5d6ffwbTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3fTff7b72.Td2a8ffwriteTb4b4b4(Ta5d6ffbTa5d6ff"Ta5d6ffATa5d6ff" Tff7b72* T79c0ff5000Tb4b4b4)

Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)

T8b949e# 2. Replace with high-entropy data (random bytes)
Tff7b72with Tffa657openTb4b4b4(Te6edf3data_fileTb4b4b4, Ta5d6ff"Ta5d6ffwbTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3fTff7b72.Td2a8ffwriteTb4b4b4(Te6edf3osTff7b72.Td2a8ffurandomTb4b4b4(T79c0ff5000Tb4b4b4)Tb4b4b4)

Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(Te6edf3is_okTb4b4b4, Ta5d6fffTa5d6ff"Ta5d6ffIntegrity should fail. Issues: Tffd700{Te6edf3issuesTffd700}Ta5d6ff"Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(
Tffa657anyTb4b4b4(Ta5d6ff"Ta5d6ffNon-linear content shiftTa5d6ff" Tff7b72in Te6edf3i Tff7b72or Ta5d6ff"Ta5d6ffEntropy ΔTa5d6ff" Tff7b72in Te6edf3i Tff7b72for Te6edf3i Tff7b72in Te6edf3issuesTb4b4b4)Tb4b4b4,
Ta5d6fffTa5d6ff"Ta5d6ffExpected entropy shift in: Tffd700{Te6edf3issuesTffd700}Ta5d6ff"Tb4b4b4,
Tb4b4b4)

Tff7b72def Td2a8fftest_ignore_patterns_extensiveTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Verify all volatile LXMF/RNS patterns are correctly filtered."""
Te6edf3volatile_files Tff7b72= Tb4b4b4[
Ta5d6ff"Ta5d6fflxmf_router/lxmf/outbound_stamp_costsTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fflxmf_router/storage/some_volatile_fileTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fflxmf_router/announces/ann_dataTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6fflxmf_router/tmp/uploadingTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdatabase.db-walTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdatabase.db-shmTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffsomething.tmpTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff.DS_StoreTa5d6ff"Tb4b4b4,
Tb4b4b4]

Tff7b72for Te6edf3v Tff7b72in Te6edf3volatile_filesTb4b4b4:
Te6edf3rel_path Tff7b72= Te6edf3PathTb4b4b4(Te6edf3vTb4b4b4)
Te6edf3full_path Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Te6edf3rel_path
Te6edf3full_pathTff7b72.Td2a8ffparentTff7b72.Td2a8ffmkdirTb4b4b4(Te6edf3parentsTff7b72=Tff7b72TrueTb4b4b4, Te6edf3exist_okTff7b72=Tff7b72TrueTb4b4b4)
Te6edf3full_pathTff7b72.Td2a8fftouchTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_should_ignoreTb4b4b4(Tffa657strTb4b4b4(Te6edf3rel_pathTb4b4b4)Tb4b4b4)Tb4b4b4,
Ta5d6fffTa5d6ff"Ta5d6ffFailed to ignore Tffd700{Te6edf3vTffd700}Ta5d6ff"Tb4b4b4,
Tb4b4b4)

Tff7b72def Td2a8fftest_critical_file_protectionTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Ensure identity and config changes are always treated as critical."""
Te6edf3id_file Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffidentityTa5d6ff"
Te6edf3id_fileTff7b72.Td2a8ffwrite_textTb4b4b4(Ta5d6ff"Ta5d6ffsecure_keyTa5d6ff"Tb4b4b4)

Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)

T8b949e# Minor modification (stays low entropy)
Te6edf3id_fileTff7b72.Td2a8ffwrite_textTb4b4b4(Ta5d6ff"Ta5d6ffsecure_kezTa5d6ff"Tb4b4b4)

Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(Te6edf3is_okTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(Tffa657anyTb4b4b4(Ta5d6ff"Ta5d6ffCritical security componentTa5d6ff" Tff7b72in Te6edf3i Tff7b72for Te6edf3i Tff7b72in Te6edf3issuesTb4b4b4)Tb4b4b4)

Tff7b72def Td2a8fftest_missing_file_detectionTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Verify missing files are detected even if not critical."""
Te6edf3misc_file Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffmisc.txtTa5d6ff"
Te6edf3misc_fileTff7b72.Td2a8ffwrite_textTb4b4b4(Ta5d6ff"Ta5d6ffdataTa5d6ff"Tb4b4b4)

Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)
Te6edf3misc_fileTff7b72.Td2a8ffunlinkTb4b4b4(Tb4b4b4)

Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(Te6edf3is_okTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(Tffa657anyTb4b4b4(Ta5d6ff"Ta5d6ffFile missing: misc.txtTa5d6ff" Tff7b72in Te6edf3i Tff7b72for Te6edf3i Tff7b72in Te6edf3issuesTb4b4b4)Tb4b4b4)

Tff7b72def Td2a8fftest_manifest_versioningTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Verify the manifest includes the new version and metadata fields."""
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)

Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffmanifest_pathTb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3manifest Tff7b72= Te6edf3jsonTff7b72.Td2a8ffloadTb4b4b4(Te6edf3fTb4b4b4)

Tff7b72selfTff7b72.Td2a8ffassertEqualTb4b4b4(Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffversionTa5d6ff"Tb4b4b4]Tb4b4b4, T79c0ff2Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertInTb4b4b4(Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4, Te6edf3manifestTb4b4b4)

T8b949e# Check if database metadata exists
Te6edf3db_rel Tff7b72= Tffa657strTb4b4b4(Tff7b72selfTff7b72.Td2a8ffdb_pathTff7b72.Td2a8ffrelative_toTb4b4b4(Tff7b72selfTff7b72.Td2a8fftest_dirTb4b4b4)Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertInTb4b4b4(Te6edf3db_relTb4b4b4, Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4]Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertInTb4b4b4(Ta5d6ff"Ta5d6ffentropyTa5d6ff"Tb4b4b4, Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3db_relTb4b4b4]Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertInTb4b4b4(Ta5d6ff"Ta5d6ffsizeTa5d6ff"Tb4b4b4, Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3db_relTb4b4b4]Tb4b4b4)

Tff7b72def Td2a8fftest_database_size_divergenceTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Verify size changes are caught when hash changes but entropy is similar."""
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)

T8b949e# Grow the database with similar content
Te6edf3conn Tff7b72= Te6edf3sqlite3Tff7b72.Td2a8ffconnectTb4b4b4(Tff7b72selfTff7b72.Td2a8ffdb_pathTb4b4b4)
Te6edf3connTff7b72.Td2a8ffexecuteTb4b4b4(Ta5d6ff"Ta5d6ffINSERT INTO data (val) VALUES (?)Ta5d6ff"Tb4b4b4, Tb4b4b4(Ta5d6ff"Ta5d6ffmore contentTa5d6ff" Tff7b72* T79c0ff100Tb4b4b4,Tb4b4b4)Tb4b4b4)
Te6edf3connTff7b72.Td2a8ffcommitTb4b4b4(Tb4b4b4)
Te6edf3connTff7b72.Td2a8ffcloseTb4b4b4(Tb4b4b4)

Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
T8b949e# Even if entropy shift is low, hash and size changed
Tff7b72if Tff7b72not Te6edf3is_okTb4b4b4:
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(Tffa657anyTb4b4b4(Ta5d6ff"Ta5d6ffDatabaseTa5d6ff" Tff7b72in Te6edf3i Tff7b72for Te6edf3i Tff7b72in Te6edf3issuesTb4b4b4)Tb4b4b4)

T8b949e# ------------------------------------------------------------------
T8b949e# Corrupt / malformed manifest
T8b949e# ------------------------------------------------------------------

Tff7b72def Td2a8fftest_corrupt_manifest_jsonTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""check_integrity must not crash on invalid JSON in the manifest."""
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)
Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffmanifest_pathTb4b4b4, Ta5d6ff"Ta5d6ffwTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3fTff7b72.Td2a8ffwriteTb4b4b4(Ta5d6ff"Ta5d6ff{{Ta5d6ff{Ta5d6ffNOT JSON!!!Ta5d6ff"Tb4b4b4)

Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(Te6edf3is_okTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(Tffa657anyTb4b4b4(Ta5d6ff"Ta5d6ffIntegrity check failedTa5d6ff" Tff7b72in Te6edf3i Tff7b72for Te6edf3i Tff7b72in Te6edf3issuesTb4b4b4)Tb4b4b4)

Tff7b72def Td2a8fftest_empty_manifest_fileTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""check_integrity must handle a 0-byte manifest gracefully."""
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)
Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffmanifest_pathTb4b4b4, Ta5d6ff"Ta5d6ffwTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3fTff7b72.Td2a8fftruncateTb4b4b4(T79c0ff0Tb4b4b4)

Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(Te6edf3is_okTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(Tffa657anyTb4b4b4(Ta5d6ff"Ta5d6ffIntegrity check failedTa5d6ff" Tff7b72in Te6edf3i Tff7b72for Te6edf3i Tff7b72in Te6edf3issuesTb4b4b4)Tb4b4b4)

Tff7b72def Td2a8fftest_manifest_missing_keysTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Manifest with valid JSON but missing expected keys should not crash."""
Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffmanifest_pathTb4b4b4, Ta5d6ff"Ta5d6ffwTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3jsonTff7b72.Td2a8ffdumpTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffversionTa5d6ff"Tb4b4b4: T79c0ff2Tb4b4b4}Tb4b4b4, Te6edf3fTb4b4b4)

Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(Te6edf3is_ok Tff7b72or Tffa657isinstanceTb4b4b4(Te6edf3issuesTb4b4b4, Tffa657listTb4b4b4)Tb4b4b4)

T8b949e# ------------------------------------------------------------------
T8b949e# Hash consistency
T8b949e# ------------------------------------------------------------------

Tff7b72def Td2a8fftest_hash_file_consistencyTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Same file content must always produce the same hash."""
Te6edf3h1 Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_hash_fileTb4b4b4(Tff7b72selfTff7b72.Td2a8ffdb_pathTb4b4b4)
Te6edf3h2 Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_hash_fileTb4b4b4(Tff7b72selfTff7b72.Td2a8ffdb_pathTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertEqualTb4b4b4(Te6edf3h1Tb4b4b4, Te6edf3h2Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertIsNotNoneTb4b4b4(Te6edf3h1Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertEqualTb4b4b4(Tffa657lenTb4b4b4(Te6edf3h1Tb4b4b4)Tb4b4b4, T79c0ff64Tb4b4b4) T8b949e# SHA-256 hex length

Tff7b72def Td2a8fftest_hash_file_missingTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""_hash_file on a missing path must return None."""
Te6edf3result Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_hash_fileTb4b4b4(Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffdoes_not_exist.binTa5d6ff"Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertIsNoneTb4b4b4(Te6edf3resultTb4b4b4)

T8b949e# ------------------------------------------------------------------
T8b949e# DB integrity check
T8b949e# ------------------------------------------------------------------

Tff7b72def Td2a8fftest_check_db_integrity_validTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""_check_db_integrity on a valid DB returns (True, 'ok')."""
Te6edf3okTb4b4b4, Te6edf3msg Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_check_db_integrityTb4b4b4(Tff7b72selfTff7b72.Td2a8ffdb_pathTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(Te6edf3okTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertEqualTb4b4b4(Te6edf3msgTb4b4b4, Ta5d6ff"Ta5d6ffokTa5d6ff"Tb4b4b4)

Tff7b72def Td2a8fftest_check_db_integrity_not_sqliteTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""_check_db_integrity on a non-SQLite file returns (False, ...)."""
Te6edf3bad Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffbad.dbTa5d6ff"
Te6edf3badTff7b72.Td2a8ffwrite_textTb4b4b4(Ta5d6ff"Ta5d6ffthis is not sqliteTa5d6ff"Tb4b4b4)
Te6edf3okTb4b4b4, Te6edf3msg Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_check_db_integrityTb4b4b4(Te6edf3badTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(Te6edf3okTb4b4b4)

Tff7b72def Td2a8fftest_check_db_integrity_missingTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""_check_db_integrity on missing file returns (False, ...)."""
Te6edf3okTb4b4b4, Te6edf3msg Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_check_db_integrityTb4b4b4(Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffgone.dbTa5d6ff"Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(Te6edf3okTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertInTb4b4b4(Ta5d6ff"Ta5d6ffdoes not existTa5d6ff"Tb4b4b4, Te6edf3msgTb4b4b4)

Tff7b72def Td2a8fftest_check_db_integrity_empty_fileTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""_check_db_integrity on a 0-byte file should not crash."""
Te6edf3empty_db Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffempty.dbTa5d6ff"
Te6edf3empty_dbTff7b72.Td2a8fftouchTb4b4b4(Tb4b4b4)
Te6edf3okTb4b4b4, Te6edf3msg Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_check_db_integrityTb4b4b4(Te6edf3empty_dbTb4b4b4)
T8b949e# SQLite treats a 0-byte file as a valid empty database
Tff7b72selfTff7b72.Td2a8ffassertIsInstanceTb4b4b4(Te6edf3okTb4b4b4, Tffa657boolTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertIsInstanceTb4b4b4(Te6edf3msgTb4b4b4, Tffa657strTb4b4b4)

T8b949e# ------------------------------------------------------------------
T8b949e# Entropy threshold boundaries
T8b949e# ------------------------------------------------------------------

Tff7b72def Td2a8fftest_entropy_threshold_db_just_belowTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""DB entropy delta of 0.99 should NOT trigger anomaly warning."""
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)

Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffmanifest_pathTb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3manifest Tff7b72= Te6edf3jsonTff7b72.Td2a8ffloadTb4b4b4(Te6edf3fTb4b4b4)

Te6edf3db_rel Tff7b72= Tffa657strTb4b4b4(Tff7b72selfTff7b72.Td2a8ffdb_pathTff7b72.Td2a8ffrelative_toTb4b4b4(Tff7b72selfTff7b72.Td2a8fftest_dirTb4b4b4)Tb4b4b4)
Te6edf3real_entropy Tff7b72= Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3db_relTb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffentropyTa5d6ff"Tb4b4b4]
Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3db_relTb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffentropyTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3real_entropy Tff7b72+ T79c0ff0.99

Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffmanifest_pathTb4b4b4, Ta5d6ff"Ta5d6ffwTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3jsonTff7b72.Td2a8ffdumpTb4b4b4(Te6edf3manifestTb4b4b4, Te6edf3fTb4b4b4)

Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(
Tffa657anyTb4b4b4(Ta5d6ff"Ta5d6ffstructural anomalyTa5d6ff" Tff7b72in Te6edf3i Tff7b72for Te6edf3i Tff7b72in Te6edf3issuesTb4b4b4)Tb4b4b4,
Ta5d6fffTa5d6ff"Ta5d6ffShould not flag anomaly at delta=0.99: Tffd700{Te6edf3issuesTffd700}Ta5d6ff"Tb4b4b4,
Tb4b4b4)

Tff7b72def Td2a8fftest_entropy_threshold_db_just_aboveTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""DB entropy delta of 1.01 should trigger anomaly warning."""
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)

Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffmanifest_pathTb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3manifest Tff7b72= Te6edf3jsonTff7b72.Td2a8ffloadTb4b4b4(Te6edf3fTb4b4b4)

Te6edf3db_rel Tff7b72= Tffa657strTb4b4b4(Tff7b72selfTff7b72.Td2a8ffdb_pathTff7b72.Td2a8ffrelative_toTb4b4b4(Tff7b72selfTff7b72.Td2a8fftest_dirTb4b4b4)Tb4b4b4)
Te6edf3real_entropy Tff7b72= Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3db_relTb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffentropyTa5d6ff"Tb4b4b4]
Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3db_relTb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffentropyTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3real_entropy Tff7b72+ T79c0ff1.01

T8b949e# Also change the file hash so it triggers the comparison path
Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6fffilesTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3db_relTb4b4b4] Tff7b72= Ta5d6ff"Ta5d6ff0Ta5d6ff" Tff7b72* T79c0ff64

Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffmanifest_pathTb4b4b4, Ta5d6ff"Ta5d6ffwTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3jsonTff7b72.Td2a8ffdumpTb4b4b4(Te6edf3manifestTb4b4b4, Te6edf3fTb4b4b4)

Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(Te6edf3is_okTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(
Tffa657anyTb4b4b4(Ta5d6ff"Ta5d6ffstructural anomalyTa5d6ff" Tff7b72in Te6edf3i Tff7b72or Ta5d6ff"Ta5d6ffEntropyTa5d6ff" Tff7b72in Te6edf3i Tff7b72for Te6edf3i Tff7b72in Te6edf3issuesTb4b4b4)Tb4b4b4,
Ta5d6fffTa5d6ff"Ta5d6ffShould flag anomaly at delta=1.01: Tffd700{Te6edf3issuesTffd700}Ta5d6ff"Tb4b4b4,
Tb4b4b4)

Tff7b72def Td2a8fftest_entropy_threshold_file_1_49_no_flagTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Non-DB file entropy delta of 1.49 should NOT trigger content shift."""
Te6edf3data_file Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffpayload.binTa5d6ff"
Te6edf3data_fileTff7b72.Td2a8ffwrite_bytesTb4b4b4(Ta5d6ffbTa5d6ff"Ta5d6ffXTa5d6ff" Tff7b72* T79c0ff2000Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)

Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffmanifest_pathTb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3manifest Tff7b72= Te6edf3jsonTff7b72.Td2a8ffloadTb4b4b4(Te6edf3fTb4b4b4)

Te6edf3rel Tff7b72= Tffa657strTb4b4b4(Te6edf3data_fileTff7b72.Td2a8ffrelative_toTb4b4b4(Tff7b72selfTff7b72.Td2a8fftest_dirTb4b4b4)Tb4b4b4)
Te6edf3real_entropy Tff7b72= Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3relTb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffentropyTa5d6ff"Tb4b4b4]
Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3relTb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffentropyTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3real_entropy Tff7b72+ T79c0ff1.49
Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6fffilesTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3relTb4b4b4] Tff7b72= Ta5d6ff"Ta5d6ff0Ta5d6ff" Tff7b72* T79c0ff64

Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffmanifest_pathTb4b4b4, Ta5d6ff"Ta5d6ffwTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3jsonTff7b72.Td2a8ffdumpTb4b4b4(Te6edf3manifestTb4b4b4, Te6edf3fTb4b4b4)

Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(
Tffa657anyTb4b4b4(Ta5d6ff"Ta5d6ffNon-linear content shiftTa5d6ff" Tff7b72in Te6edf3i Tff7b72for Te6edf3i Tff7b72in Te6edf3issuesTb4b4b4)Tb4b4b4,
Ta5d6fffTa5d6ff"Ta5d6ffShould not flag content shift at delta=1.49: Tffd700{Te6edf3issuesTffd700}Ta5d6ff"Tb4b4b4,
Tb4b4b4)

Tff7b72def Td2a8fftest_entropy_threshold_file_1_51_flagsTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""Non-DB file entropy delta of 1.51 should trigger content shift."""
Te6edf3data_file Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffpayload.binTa5d6ff"
Te6edf3data_fileTff7b72.Td2a8ffwrite_bytesTb4b4b4(Ta5d6ffbTa5d6ff"Ta5d6ffXTa5d6ff" Tff7b72* T79c0ff2000Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)

Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffmanifest_pathTb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3manifest Tff7b72= Te6edf3jsonTff7b72.Td2a8ffloadTb4b4b4(Te6edf3fTb4b4b4)

Te6edf3rel Tff7b72= Tffa657strTb4b4b4(Te6edf3data_fileTff7b72.Td2a8ffrelative_toTb4b4b4(Tff7b72selfTff7b72.Td2a8fftest_dirTb4b4b4)Tb4b4b4)
Te6edf3real_entropy Tff7b72= Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3relTb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffentropyTa5d6ff"Tb4b4b4]
Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6ffmetadataTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3relTb4b4b4]Tb4b4b4[Ta5d6ff"Ta5d6ffentropyTa5d6ff"Tb4b4b4] Tff7b72= Te6edf3real_entropy Tff7b72+ T79c0ff1.51
Te6edf3manifestTb4b4b4[Ta5d6ff"Ta5d6fffilesTa5d6ff"Tb4b4b4]Tb4b4b4[Te6edf3relTb4b4b4] Tff7b72= Ta5d6ff"Ta5d6ff0Ta5d6ff" Tff7b72* T79c0ff64

Tff7b72with Tffa657openTb4b4b4(Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffmanifest_pathTb4b4b4, Ta5d6ff"Ta5d6ffwTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3jsonTff7b72.Td2a8ffdumpTb4b4b4(Te6edf3manifestTb4b4b4, Te6edf3fTb4b4b4)

Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertFalseTb4b4b4(Te6edf3is_okTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(
Tffa657anyTb4b4b4(Ta5d6ff"Ta5d6ffNon-linear content shiftTa5d6ff" Tff7b72in Te6edf3i Tff7b72for Te6edf3i Tff7b72in Te6edf3issuesTb4b4b4)Tb4b4b4,
Ta5d6fffTa5d6ff"Ta5d6ffShould flag content shift at delta=1.51: Tffd700{Te6edf3issuesTffd700}Ta5d6ff"Tb4b4b4,
Tb4b4b4)

T8b949e# ------------------------------------------------------------------
T8b949e# DB outside storage_dir
T8b949e# ------------------------------------------------------------------

Tff7b72def Td2a8fftest_db_outside_storage_dirTb4b4b4(Tff7b72selfTb4b4b4)Tb4b4b4:
T8b949e"""check_integrity must not crash when DB is outside storage_dir."""
Tff7b72import T7ee787tempfile Tff7b72as T7ee787tf

Te6edf3ext_dir Tff7b72= Te6edf3PathTb4b4b4(Te6edf3tfTff7b72.Td2a8ffmkdtempTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3ext_db Tff7b72= Te6edf3ext_dir Tff7b72/ Ta5d6ff"Ta5d6ffexternal.dbTa5d6ff"
Te6edf3conn Tff7b72= Te6edf3sqlite3Tff7b72.Td2a8ffconnectTb4b4b4(Te6edf3ext_dbTb4b4b4)
Te6edf3connTff7b72.Td2a8ffexecuteTb4b4b4(Ta5d6ff"Ta5d6ffCREATE TABLE t (id INTEGER PRIMARY KEY)Ta5d6ff"Tb4b4b4)
Te6edf3connTff7b72.Td2a8ffcloseTb4b4b4(Tb4b4b4)

Te6edf3mgr Tff7b72= Te6edf3IntegrityManagerTb4b4b4(Tff7b72selfTff7b72.Td2a8fftest_dirTb4b4b4, Te6edf3ext_dbTb4b4b4)
Te6edf3mgrTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)
Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Te6edf3mgrTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(Te6edf3is_ok Tff7b72or Tffa657isinstanceTb4b4b4(Te6edf3issuesTb4b4b4, Tffa657listTb4b4b4)Tb4b4b4)
Tff7b72finallyTb4b4b4:
Te6edf3shutilTff7b72.Td2a8ffrmtreeTb4b4b4(Te6edf3ext_dirTb4b4b4)

T8b949e# ------------------------------------------------------------------
T8b949e# Hypothesis: entropy for any binary data
T8b949e# ------------------------------------------------------------------

Tf0883e@settingsTb4b4b4(
Te6edf3suppress_health_checkTff7b72=Tb4b4b4[Te6edf3HealthCheckTff7b72.Td2a8fftoo_slowTb4b4b4]Tb4b4b4,
Te6edf3deadlineTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3derandomizeTff7b72=Tff7b72TrueTb4b4b4,
Tb4b4b4)
Tf0883e@givenTb4b4b4(Te6edf3stTff7b72.Td2a8ffbinaryTb4b4b4(Te6edf3min_sizeTff7b72=T79c0ff1Tb4b4b4, Te6edf3max_sizeTff7b72=T79c0ff4096Tb4b4b4)Tb4b4b4)
Tff7b72def Td2a8fftest_entropy_monotonic_with_unique_bytesTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3dataTb4b4b4)Tb4b4b4:
T8b949e"""More unique byte values should produce higher entropy."""
Te6edf3f Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffhyp_entTa5d6ff"
Te6edf3fTff7b72.Td2a8ffwrite_bytesTb4b4b4(Te6edf3dataTb4b4b4)
Te6edf3e Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ff_calculate_entropyTb4b4b4(Te6edf3fTb4b4b4)
Te6edf3unique Tff7b72= Tffa657lenTb4b4b4(Tffa657setTb4b4b4(Te6edf3dataTb4b4b4)Tb4b4b4)
Tff7b72if Te6edf3unique Tff7b72== T79c0ff1Tb4b4b4:
Tff7b72selfTff7b72.Td2a8ffassertAlmostEqualTb4b4b4(Te6edf3eTb4b4b4, T79c0ff0.0Tb4b4b4, Te6edf3placesTff7b72=T79c0ff5Tb4b4b4)
Tff7b72elseTb4b4b4:
Tff7b72selfTff7b72.Td2a8ffassertGreaterTb4b4b4(Te6edf3eTb4b4b4, T79c0ff0.0Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertLessEqualTb4b4b4(Te6edf3eTb4b4b4, T79c0ff8.0 Tff7b72+ T79c0ff1e-9Tb4b4b4)

T8b949e# ------------------------------------------------------------------
T8b949e# Hypothesis: save_manifest then check_integrity always consistent
T8b949e# ------------------------------------------------------------------

Tf0883e@settingsTb4b4b4(
Te6edf3suppress_health_checkTff7b72=Tb4b4b4[Te6edf3HealthCheckTff7b72.Td2a8fftoo_slowTb4b4b4]Tb4b4b4,
Te6edf3deadlineTff7b72=Tff7b72NoneTb4b4b4,
Te6edf3max_examplesTff7b72=T79c0ff10Tb4b4b4,
Te6edf3derandomizeTff7b72=Tff7b72TrueTb4b4b4,
Tb4b4b4)
Tf0883e@givenTb4b4b4(Te6edf3stTff7b72.Td2a8ffbinaryTb4b4b4(Te6edf3min_sizeTff7b72=T79c0ff1Tb4b4b4, Te6edf3max_sizeTff7b72=T79c0ff512Tb4b4b4)Tb4b4b4)
Tff7b72def Td2a8fftest_save_then_check_always_passesTb4b4b4(Tff7b72selfTb4b4b4, Te6edf3extra_dataTb4b4b4)Tb4b4b4:
T8b949e"""After save_manifest(), check_integrity() must pass for unchanged state."""
Te6edf3extra_file Tff7b72= Tff7b72selfTff7b72.Td2a8fftest_dir Tff7b72/ Ta5d6ff"Ta5d6ffextra.binTa5d6ff"
Te6edf3extra_fileTff7b72.Td2a8ffwrite_bytesTb4b4b4(Te6edf3extra_dataTb4b4b4)
Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)
Te6edf3is_okTb4b4b4, Te6edf3issues Tff7b72= Tff7b72selfTff7b72.Td2a8ffmanagerTff7b72.Td2a8ffcheck_integrityTb4b4b4(Tb4b4b4)
Tff7b72selfTff7b72.Td2a8ffassertTrueTb4b4b4(Te6edf3is_okTb4b4b4, Ta5d6fffTa5d6ff"Ta5d6ffShould pass after save. Issues: Tffd700{Te6edf3issuesTffd700}Ta5d6ff"Tb4b4b4)


Tff7b72if Tff7b72__name__ Tff7b72== Ta5d6ff"Ta5d6ff__main__Ta5d6ff"Tb4b4b4:
Te6edf3unittestTff7b72.Td2a8ffmainTb4b4b4(Tb4b4b4)


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────